home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / listings / v_13_06 / dwyer / randcomb.c < prev    next >
Text File  |  1995-02-10  |  5KB  |  208 lines

  1. /*    rand_com[b].c
  2.     combination multiplicative random number generator
  3.         subtract two random numbers modulo moduli1-1 and shuffle
  4.     see
  5.         L'Ecuyer, Comm. of the ACM 1988 vol. 31
  6.         Numerical Recipes in C, 2nd edition, pp.  278-86
  7.         shuffling -- Knuth, vol. II
  8.     Copyright (c) 1994, 1995 by Gerald P. Dwyer, Jr.                */
  9.  
  10. #include    <time.h>
  11. #include    <stdlib.h>
  12. #include    <float.h>
  13. #include    <assert.h>
  14.  
  15. #define    TESTING
  16.  
  17. #define    TRUE (-1)
  18. #define    FALSE 0
  19.  
  20. void init_rand_comb(long *seed1, long *seed2) ;
  21. long get_init_rand(int) ;
  22. long rand_comb(void) ;
  23. long genr_rand_diff(void) ;
  24. long    genr_rand(long a, long x, long modulus, long q, long r) ;
  25.  
  26.                         /* first generator */
  27. #define    MOD1         2147483563L        /* modulus */
  28. #define    MULT1         40014L    /* multiplier */
  29.                         /* modulus=multiplier*quotient+remainder */
  30. #define    Q1             53668L    /* quotient = [modulus/multiplier] */
  31. #define    R1             12211L    /* remainder */
  32.  
  33.                         /* second generator */
  34. #define    MOD2         2147483399L
  35. #define    MULT2         40692L
  36. #define    Q2             52774L
  37. #define    R2               3791L
  38.  
  39. #define    MOD_COMB        (MOD1-1)
  40.  
  41. #define    MIN_VALUE1          1
  42. #define    MAX_VALUE1  (MOD1-1)
  43. #define    MIN_VALUE2          1
  44. #define    MAX_VALUE2  (MOD2-1)
  45. #define    MAX_VALUE        ( (MOD1<MOD2) ? MAX_VALUE1 : MAX_VALUE2)
  46. #define    EXP_VAL        804307721L
  47.  
  48. #define    GENR1(init_rand)    genr_rand(MULT1, init_rand, MOD1, Q1, R1)
  49. #define    GENR2(init_rand)    genr_rand(MULT2, init_rand, MOD2, Q2, R2)
  50.  
  51. #define    IMPOSSIBLE_RAND    (-1)
  52. #define    STARTUP_RANDS       16   /* throw away initial draws */
  53. #define    DIM_RAND            150   /* size of array shuffled */
  54.  
  55.  
  56. static long rand1, rand2 ;
  57. static long rand_num = IMPOSSIBLE_RAND ;
  58. static long rands[DIM_RAND] ;
  59.  
  60.  
  61. /* initialize generators with seeds
  62.     fill rands[] with initial values  */
  63. void init_rand_comb(long *seed1, long *seed2)
  64. {
  65.     extern long rand1, rand2 ;
  66.     extern long rand_num ;
  67.     extern long rands[] ;
  68.     int i ;
  69.  
  70.     if (*seed1 <= 0 || *seed1 > MAX_VALUE1)
  71.         *seed1 = get_init_rand(MAX_VALUE1) ;
  72.     if (*seed2 <= 0 || *seed2 > MAX_VALUE2)
  73.         *seed2 = get_init_rand(MAX_VALUE2) ;
  74.  
  75.                         /* seed the routine */
  76.     rand1 = *seed1 ;
  77.     rand2 = *seed2 ;
  78.     genr_rand_diff() ;
  79.  
  80.     for (i = 1; i < STARTUP_RANDS; i++)    /* throw some away */
  81.         genr_rand_diff() ;
  82.                     /* fill the array of randum number values */
  83.     for (i = 0; i < DIM_RAND; i++)
  84.         rands[i] = genr_rand_diff() ;
  85.                     /* initialize rand_num for shuffling */
  86.     rand_num = rands[DIM_RAND-1] ;
  87. }
  88.  
  89.  
  90. /* get a long initial seed for generator
  91.     assumes that rand returns a short integer */
  92. long get_init_rand(int max_value)
  93. {
  94.     long seed ;
  95.  
  96.     srand((unsigned int)time(NULL)) ;    /* initialize system generator */
  97.     do {
  98.         seed  = ((long)rand())*rand() ;
  99.         seed += ((long)rand())*rand() ;
  100.     } while (seed > max_value) ;
  101.  
  102.     assert(seed > 0) ;
  103.  
  104.     return seed ;
  105. }
  106.  
  107.  
  108. /* generate the difference between random numbers
  109.     assumes    0 <  rand1    <  MOD1
  110.             0 <  rand2    <  MOD2
  111.     output       1 <= rand_num <= MOD_COMB */
  112. long genr_rand_diff(void)
  113. {
  114.     extern long rand1, rand2 ;
  115.     long rand_new ;
  116.  
  117.     rand1 = GENR1(rand1) ;
  118.     rand2 = GENR2(rand2) ;
  119.     rand_new = rand1 - rand2 ;
  120.     if (rand_new <= 0)
  121.         rand_new += MOD_COMB ;
  122.  
  123.     assert(rand_new >= 1 && rand_new <= MOD_COMB) ;
  124.  
  125.     return rand_new ;
  126. }
  127.  
  128.  
  129. /* generate the next value in sequence from generator
  130.     uses approximate factoring
  131.     residue = (a * x) mod modulus
  132.             = a*x - [(a*x)/modulus]*modulus
  133.     where
  134.         [(a*x)/modulus] = integer less than or equal to (a*x)/modulus
  135.     approximate factoring avoids overflow associated with a*x and
  136.         uses equivalence of above with
  137.     residue = a * (x - q * k) - r * k + (k-k1) * modulus
  138.     where
  139.         modulus = a * q + r
  140.         q  = [modulus/a]
  141.         k  = [x/q]        (= [ax/aq])
  142.         k1 = [a*x/m]
  143.     assumes
  144.         a, m > 0
  145.         0 < init_rand < modulus
  146.         a * a <= modulus
  147.         [a*x/a*q]-[a*x/modulus] <= 1
  148.             (for only one addition of modulus below) */
  149. long    genr_rand(long a, long x, long modulus, long q, long r)
  150. {
  151.     long k, residue ;
  152.  
  153.     k = x / q ;
  154.     residue = a * (x - q * k) - r * k ;
  155.     if (residue < 0)
  156.         residue += modulus ;
  157.  
  158.     assert(residue >= 1 && residue <= modulus-1) ;
  159.  
  160.     return residue ;
  161. }
  162.  
  163.  
  164. /* get a random number from rands[] and replace it*/
  165. long rand_comb(void)
  166. {
  167.     extern long rand_num ;
  168.     extern long rands[] ;
  169.     int i ;
  170.                 /* if not initialized, do it now */
  171.     if (rand_num == IMPOSSIBLE_RAND) {
  172.         rand_num = 1 ;
  173.         init_rand_comb(&rand_num, &rand_num) ;
  174.     }
  175.  
  176.     /* rand_num from previous call determines next rand_num from rands[] */
  177.     i = (int) (((double)DIM_RAND*rand_num)/(double)(MAX_VALUE)) ;
  178.     rand_num = rands[i] ;
  179.  
  180.         /* replace random number used */
  181.     rands[i] = genr_rand_diff() ;
  182.  
  183.     return rand_num ;
  184. }
  185.  
  186.  
  187. #if    defined(TESTING)
  188. /* Test the generator */
  189. #include     <stdio.h>
  190. void main(void)
  191. {
  192.     long seed1=1, seed2=1 ;
  193.     int i ;
  194.  
  195.     init_rand_comb(&seed1, &seed2) ;
  196.     printf("Seeds for random number generator are %ld   %ld\n",
  197.             seed1, seed2) ;
  198.     i = STARTUP_RANDS + DIM_RAND ;
  199.     do {
  200.         rand_comb() ;
  201.         i++ ;
  202.     } while (i < 9999) ;
  203.  
  204.     printf("On draw 10000, random number should be %ld\n", EXP_VAL) ;
  205.     printf("On draw %d, random number is        %ld\n", i+1, rand_comb()) ;
  206. }
  207. #endif TESTING
  208.